package com.billionsfinance.hessian.common.machine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * 分布式环境中的每一个服务资源该如何处理
 *
 * @ ClassName: MachineResource.java
 * @ Author: WeiHui-Z
 * @ Date: 2016/11/24 11:07
 * @ Version: v1.0.0
 */
public final class MachineResource {

    private static final Logger LOGGER = LoggerFactory.getLogger(MachineResource.class);

    /**
     * 偏移值
     */
    private static final Long LongValue = (long) (0 - Integer.MIN_VALUE);

    /**
     * 读写锁
     */
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * k-服务名字,v-提供服务的机器列表
     */
    private Map<String, List<String>> serviceList = new HashMap<>();

    /**
     * k-服务名称,v--服务列表索引，下一次使用哪台机器上的服务
     */
    private Map<String, AtomicInteger> serviceIndex = new HashMap<>();

    /**
     * 构造方法私有化
     */
    private MachineResource() {

    }

    private static class InstanceHolder {

        private static MachineResource INSTANCE = new MachineResource();
    }

    /**
     * 采用静态内部类的单例模式
     */
    public static MachineResource getInstance() {
        return InstanceHolder.INSTANCE;
    }

    /**
     * 增加服务--启动时增加服务信息
     *
     * @param service-服务
     * @param provider-服务提供方
     */
    public void addMachineResource(String service, String provider) {
        try {
            lock.writeLock().lock();
            List<String> providerList = serviceList.get(service);
            if (providerList == null || providerList.isEmpty()) {
                //第一次加载服务
                providerList = new ArrayList<>();
                // 建立下标,下标是指下一个要拿的机器的序号，以-1开始
                serviceIndex.put(service, new AtomicInteger(-1));
            }
            providerList.add(provider);
            serviceList.put(service, providerList);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 删除服务
     *
     * @param service
     * @param provider
     */
    public void removeMachineResource(String service, String provider) {
        try {
            lock.writeLock().lock();
            List<String> providerList = serviceList.get(service);
            if (providerList != null) {
                // 先删除机器
                providerList.remove(provider);
                // 如果列表为空，也删除列表和下标--服务下线
                if (providerList.isEmpty()) {
                    serviceList.remove(service);
                    serviceIndex.remove(service);
                }
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public String getResource(String service) {
        String provider = null;
        try {
            // 获取锁
            lock.readLock().lock();
            // 拿取机器列表
            List<String> machineList = serviceList.get(service);
            if (null == machineList) {
                throw new Exception("no machineList found for :" + service);
            }
            // 计算机器下标
            AtomicInteger index = serviceIndex.get(service);
            int newIndex = index.incrementAndGet();// [-2147483648,
            // 修正
            if (newIndex < 0) {// 务必要进行修正// 最大值的话，就要重新开始,否则会成为负数
                newIndex = (int) ((long) newIndex + LongValue);
            }
            // 应用
            newIndex = newIndex % machineList.size();
            // 拿取目标machine
            provider = machineList.get(newIndex);
        } catch (Exception e) {
            LOGGER.error("获取[{}]服务时出现了一个错误.", service, e);
        } finally {// 释放锁
            lock.readLock().unlock();
        }
        return provider;
    }

    /**
     * 导出服务详细信息列表
     *
     * @return
     */
    public Map<String, List<String>> getServicesDetail() {
        Map<String, List<String>> result = new HashMap<>();
        Set<Map.Entry<String, List<String>>> entries = serviceList.entrySet();
        for (Map.Entry<String, List<String>> entry : entries) {
            String key = entry.getKey();
            List<String> value = entry.getValue();
            List<String> list = new ArrayList<>();
            for (String s : value) {
                list.add(s);
            }
            result.put(key, list);
        }
        return result;
    }

}